#ifndef __MIBAND_4_H__
#define __MIBAND_4_H__
#include <Arduino.h>
#include "A_Config.h"
#include <string>
#include <NimBLEDevice.h>
#include <WiFi.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>
#include "opus.h"

#define VOICE_MALLOC_SIZE 1024 * 15
#define LOG_PRINT Serial.printf

typedef enum
{
    WECHAT = 0,
    PENGUIN_1 = 1,
    MI_CHAT_2 = 2,
    FACEBOOK = 3,
    TWITTER = 4,
    MI_APP_5 = 5,
    SNAPCHAT = 6,
    WHATSAPP = 7,
    RED_WHITE_FIRE_8 = 8,
    CHINESE_9 = 9,
    ALARM_CLOCK = 10,
    APP_11 = 11,
    INSTAGRAM = 12,
    CHAT_BLUE_13 = 13,
    COW_14 = 14,
    DUMMY15 = 15,
    YOUTUBE = 16,
    STAR_17 = 17,
    APP_18 = 18,
    DUMMY_19 = 19,
    CHINESE_20 = 20,
    CALENDAR = 21,
    FACEBOOK_MESSENGER = 22,
    VIBER = 23,
    LINE = 24,
    TELEGRAM = 25,
    KAKAOTALK = 26,
    SKYPE = 27,
    VKONTAKTE = 28,
    POKEMONGO = 29,
    HANGOUTS = 30,
    MI_31 = 31,
    CHINESE_32 = 32,
    TIM = 33,
    EMAIL = 34,
    WEATHER = 35,
    HR_WARNING_36 = 36
} miband_app_icon_t;
typedef enum
{
    CLEAR_SKY = 0,
    SCATTERED_CLOUDS = 1,
    CLOUDY = 2,
    RAIN_WITH_SUN = 3,
    THUNDERSTORM = 4,
    HAIL = 5,
    RAIN_AND_SNOW = 6,
    LIGHT_RAIN = 7,
    MEDIUM_RAIN = 8,
    HEAVY_RAIN = 9,
    EXTREME_RAIN = 10,
    SUPER_EXTREME_RAIN = 11,
    TORRENTIAL_RAIN = 12,
    SNOW_AND_SUN = 13,
    LIGHT_SNOW = 14,
    MEDIUM_SNOW = 15,
    HEAVY_SNOW = 16,
    EXTREME_SNOW = 17,
    MIST = 18,
    DRIZZLE = 19,
    WIND_AND_RAIN = 20
} miband_weather_icon_t;
typedef uint8_t miband_alarm_repeat_t;
#define MIBAND_ALARM_REPEAT_DEL 0x00
#define MIBAND_ALARM_REPEAT_ONCE 0x80
#define MIBAND_ALARM_REPEAT_MONDAY 0x01
#define MIBAND_ALARM_REPEAT_TUESDAY 0x02
#define MIBAND_ALARM_REPEAT_WEDNESDAY 0x04
#define MIBAND_ALARM_REPEAT_THURSDAY 0x08
#define MIBAND_ALARM_REPEAT_FRIDAY 0x10
#define MIBAND_ALARM_REPEAT_SATURDAY 0x20
#define MIBAND_ALARM_REPEAT_SUNDAY 0x40

typedef uint32_t miband_remind_config_t;
#define MIBAND_REMIND_CONFIG_ENABLE 0x0001
#define MIBAND_REMIND_CONFIG_HASTEXT 0x0008
#define MIBAND_REMIND_CONFIG_REPEAT_MONDAY 0x0020
#define MIBAND_REMIND_CONFIG_REPEAT_TUESDAY 0x0040
#define MIBAND_REMIND_CONFIG_REPEAT_WEDNESDAY 0x0080
#define MIBAND_REMIND_CONFIG_REPEAT_THURSDAY 0x0100
#define MIBAND_REMIND_CONFIG_REPEAT_FRIDAY 0x0200
#define MIBAND_REMIND_CONFIG_REPEAT_SATURDAY 0x0400
#define MIBAND_REMIND_CONFIG_REPEAT_SUNDAY 0x0800
#define MIBAND_REMIND_CONFIG_REPEAT_MONTHLY 0x1000
#define MIBAND_REMIND_CONFIG_REPEAT_YEARLY 0x2000

typedef struct
{
    miband_weather_icon_t icon;
    int8_t maxTemp;
    int8_t minTemp;
    char *weather_desc;
} miband_weather_t;

#pragma pack(1)
typedef struct
{
    uint16_t year; // 重要：这里的年份格式是：“2022”而不是“22”
    uint8_t month;
    uint8_t day;
    uint8_t hour;
    uint8_t minute;
    uint8_t second;
} miband_time_t;

typedef struct
{
    uint8_t dummy1;
    uint8_t percent;
    bool charging;
    miband_time_t last_off;
    uint8_t dummy2;
    miband_time_t last_charge;
    uint8_t dummy3;
    uint8_t last_level;
} miband_battery_t;
#pragma pack()

class BaiduThings
{
private:
    const char *url_baidu_token = "http://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&" BAIDU_CLIENT_ID;
    const String url_baidu_asr = "http://vop.baidu.com/pro_api?dev_pid=80001&cuid=ESP32&token=";
    const String url_baidu_bot = "https://aip.baidubce.com/rpc/2.0/unit/service/v3/chat?access_token=";
    String token = "";
    HTTPClient http;
    char *str_json;

public:
    String bot_session_id = "";
    bool hasToken()
    {
        return !(token == "");
    }
    String last_chat_remain = "";
    bool getToken();
    String getAsr(uint8_t *pcm, size_t size);
    bool getBot(String s);
    DynamicJsonDocument doc = DynamicJsonDocument(15360);
    JsonObject obj;
};

class MiBand4
{
private:
    /* data */
    char mac_addr[18];
    bool isinXiaoAi = false;
    uint8_t key[16];
    void subscribeVoice();

public:
    OpusDecoder *dec;
    const char *MIBAND_TCP_HOST;
    int MIBAND_TCP_PORT;
    uint8_t *voiceData = NULL;
    size_t voiceDataSize; // 这是目前收到的语音数据的大小
    bool voiceFinished = false;
    bool weatherUpdateReq = false;
    NimBLEClient *mibandClient;
    int16_t *PCMData;
    uint8_t getHeartRate();
    uint16_t getSteps();
    uint16_t getCalories();
    uint16_t getDistance();
    // TODO:手动更新步数信息
    // void refreshStatus();
    /**
     * @brief 获取手环电池信息
     *
     * @param result
     */
    void getBattery(miband_battery_t *result);
    /**
     * @brief 设置目标手环的MAC地址
     *
     * @param mac MAC地址
     */
    void setMAC(const char *mac);
    /**
     * @brief 设置连接密钥
     *
     * @param key 128-bit aes密钥，可以从freemyband上获取。注意：如果手环重置需要重新设置密钥
     */
    void setKey(const uint8_t *key);
    /**
     * @brief 初始化蓝牙BLE
     *
     */
    void init();
    /**
     * @brief 连接手环
     *
     * @return true 连接成功
     * @return false 连接失败
     */
    bool connect();
    /**
     * @brief 断开连接手环并关闭BLE
     *
     */
    void disconnect();
    /**
     * @brief 分块发送某数据
     *
     * @param type 消息类型
     * @param data 数据内容指针
     * @param size 数据长度
     */
    void writeChunked(uint8_t type, const uint8_t *data, size_t size);
    /**
     * @brief 发送APP通知
     *
     * @param title 通知标题
     * @param message 通知内容
     * @param ico 通知图标，enum
     */
    void sendNotify(const char *title, const char *message, miband_app_icon_t ico);
    /**
     * @brief 设置手环闹钟
     *
     * @param slot 闹钟存储位置
     * @param hour
     * @param minute
     * @param repeat MIBAND_ALARM_REPEAT_
     * @param enabled 是否使能此闹钟
     * @param snooze 是否启用小睡模式
     */
    void setAlarm(uint8_t slot, uint8_t hour, uint8_t minute, miband_alarm_repeat_t repeat = MIBAND_ALARM_REPEAT_ONCE, bool enabled = true, bool snooze = true);
    /**
     * @brief 设置事件提醒
     *
     * @param slot 事件提醒存放位置
     * @param year
     * @param month
     * @param day
     * @param hour
     * @param minute
     * @param message 提醒内容
     * @param config 提醒配置：MIBAND_REMIND_CONFIG_
     */
    void setRemind(uint8_t slot, uint8_t year, uint8_t month, uint8_t day, uint8_t hour, uint8_t minute, const char *message, miband_remind_config_t config = MIBAND_REMIND_CONFIG_ENABLE | MIBAND_REMIND_CONFIG_HASTEXT);
    /**
     * @brief 设置时间
     *
     * @param year 年，注意这里的格式与 miband_time_t 不同，比如2022年就是“22”
     * @param month
     * @param day
     * @param ISOWeek 星期，1表示周一，7表示周日
     * @param hour
     * @param minute
     * @param second
     */
    void setTime(uint8_t year, uint8_t month, uint8_t day, uint8_t ISOWeek, uint8_t hour, uint8_t minute, uint8_t second);
    /**
     * @brief 发送天气信息
     *
     * @param updateTime 天气更新时间戳
     * @param weather 天气内容
     * @param count 天气预报的天数
     * @param timezonehour 时区，以小时为单位
     */
    void setWeather(time_t updateTime, miband_weather_t *weather, uint8_t count = 7, int8_t timezonehour = 8);
    /**
     * @brief 设置显示的天气城市
     *
     * @param location 城市名
     */
    void setWeatherLocation(const char *location);
    /**
     * @brief 申请语音缓冲区
     *
     */
    void mallocVoiceMem()
    {
        if (voiceData)
            return;
        voiceData = (uint8_t *)ps_malloc(VOICE_MALLOC_SIZE);
    PCMData = (int16_t *)ps_malloc(1 * 1024 * 1024);
    }
    /**
     * @brief 释放语音缓冲区
     *
     */
    void freeVoiceMem()
    {
        if (voiceData)
        {
            free(voiceData);
            voiceData = 0;
        }
    }
    /**
     * @brief 设置是否传输语音数据
     *
     * @param start true代表开始传输语音，false表示停止
     */
    void enableRecording(bool start);

    /**
     * @brief 发送小爱同学结果文本
     *
     * @param text 要发送的文本
     */
    void sendXiaoAiResultText(const char *text);
    ////////////////////////Callback////////////////////////////////
    static void authNotifyCallback(NimBLERemoteCharacteristic *pBLERemoteCharacteristic, uint8_t *pData, size_t length, bool isNotify);
    static void statusNotifyCallback(NimBLERemoteCharacteristic *pBLERemoteCharacteristic, uint8_t *pData, size_t length, bool isNotify);
    static void heartRateNotifyCallback(NimBLERemoteCharacteristic *pBLERemoteCharacteristic, uint8_t *pData, size_t length, bool isNotify);
    static void voiceEnterExitCallback(NimBLERemoteCharacteristic *pBLERemoteCharacteristic, uint8_t *pData, size_t length, bool isNotify);
    static void voiceControlCallback(NimBLERemoteCharacteristic *pBLERemoteCharacteristic, uint8_t *pData, size_t length, bool isNotify);
    static void voiceDataCallback(NimBLERemoteCharacteristic *pBLERemoteCharacteristic, uint8_t *pData, size_t length, bool isNotify);
};
extern MiBand4 miband;

#endif